﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Sgml.Validation.Rules;
using Engine.Dtd.Schema;
using Engine.Sgml.Allocation;
using System.Diagnostics;

namespace Engine.Sgml.Validation {
    internal sealed class StartElementValidator : IElementValidator {
        private ValidationRuleCollection validationRules;
        private TraceSource traceSource;

        public StartElementValidator(ValidationRuleCollection rules, TraceSource traceSource) {
            Guard.ArgumentNotNull(rules, "rules");
            Guard.ArgumentNotNull(traceSource, "traceSource");
            this.validationRules = rules;
            this.traceSource = traceSource;
            }

        public SElement Validate(SElement element, SElement node) {
            // Установить узел предок для 
            // проверяемого узла
            Debug.Assert(element.Parent == node);
            IValidationRule elementRule = validationRules[element];
            // Установить игнорировать ли узел
            if (elementRule.Ignore(element, node)) {
                traceSource.TraceIgnoreElement(element, node);
                // Удалить узел из дерева документа
                node.Nodes.Pop();
                // Завершить работу метода
                return node;
                }
            // Если узел является пустым согласно модели содержимого
            if (elementRule.IsEmpty(element)) {
                // Если узел предок не может содержать
                // проверяемый узел
                element.IsEmpty = true;
                return node;
                }
            IValidationRule parentRule = validationRules[node];
            // Если узел предок не может содержать
            // проверяемый узел документа
            if (!parentRule.CanContain(element, node)) {
                if (node.EndTagOptional() && node.Parent.NodeType != SgmlNodeType.Document) {
                    SElement ancestor = node.Parent;
                    element.MoveToAncestor(ancestor);
                    while (!validationRules[ancestor].CanContain(element, ancestor) && ancestor.EndTagOptional()) {
                        element.MoveToAncestor(ancestor.Parent);
                        ancestor = ancestor.Parent;
                        if (ancestor == null || ancestor.NodeType == SgmlNodeType.Document)
                            break;
                        }
                    }
                }
            // Установить узел предок
            if (!element.IsEmpty)
                node = element;
            return node;
            }
        }
    }
